package org.NooLab.texx.components;



import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


import org.NooLab.structures.texx.plugg.algo.intf.AlgoAssignatorIntf;
import org.NooLab.structures.texx.plugg.algo.intf.AlgoReaderIntf;
import org.NooLab.structures.texx.plugg.algo.intf.AlgorithmIntf;
import org.NooLab.structures.texx.plugg.algo.intf.AlgorithmParameters;
import org.NooLab.texx.TexxPluginSettings;
import org.NooLab.texx.TexxPluginSettingsIntf;
import org.NooLab.texx.plugg.TransformationEnvIntf;
import org.NooLab.texx.plugg.algo.clazz.AlgoClassLoader;
import org.NooLab.texx.plugg.algo.intf.AlgoColumnWriterIntf;
import org.NooLab.texx.plugg.algo.intf.AlgoMeasurementIntf;
import org.NooLab.texx.properties.TexxAppGeneralPropertiesIntf;
import org.NooLab.texx.properties.TexxProperties;


/**
 * 
 * 
 * This class hosts the classloader for plugged algorithms
 * 
 * (in SomFluid, this is provided by StackedTransformation)
 * 
 * The pluginsettings maintain a transient reference to this
 * 
 *
 */
public class AlgorithmsProvider implements AlgorithmsProviderIntf {

	TexxProperties texxProperties;
	TexxPluginSettings tp;
	
  	TexxPluginSettingsIntf pluginSettings;
	TexxAppGeneralPropertiesIntf texAppProperties;

	Map<String,Object> algorithMap = new HashMap<String, Object>();

	ArrayList<AlgorithmDescription> algorithms = new ArrayList<AlgorithmDescription>(); 

	private transient TransformationEnvIntf transformOriginator ;
	
	/** its indeed an ID , not an index position ! */
	int serialID = -1; 
	String idString;
	
	
	/**  this package path we have to make dynamic */ 
	String packagePath = "org.NooLab.somtransform.algo." ;

	
	/** Reflection is used to create an instance  */
	String algorithmName = "";
	transient Object algorithm ;

	/** this serves as a transfer container when the transformations are serialized  */
	AlgorithmParameters algoParameters;

	
	// ========================================================================
	public AlgorithmsProvider(TexxProperties props){
		texxProperties = props;	
		init();
	}
	// ========================================================================
	
	
	private void init(){
		
		pluginSettings = texxProperties.getPluginSettings() ;
		
		transformOriginator = pluginSettings.getTransformationOriginator() ;
		
		if (pluginSettings != null){
			
			// Class clzz = pluginSettings.getPluginClassByName( "PdfReader" );
		}
	}
	
	
	public int createAlgoObjects( int algotype) {
		// 
		int z=0;
		pluginSettings.getAlgoDeclarations();
		ArrayList<String> loadedclasses = pluginSettings.getLoadedPluginClasses() ;
		
		int n = loadedclasses.size() ;
		
		for (int i=0;i<n;i++){
			String algoname = loadedclasses.get(i) ;
			
			// XXXXXXXXXXX later: dynamic !! from catalog...
			try {

				algorithm = createAlgoObject(algoname, algotype);
				
				if (algorithm != null){
					algorithMap.put( algoname, algorithm);
					
					// ArrayList<AlgorithmDescription> 
					AlgorithmDescription ad = new AlgorithmDescription();
					ad.name = algoname;
					ad.instance = algorithm;
					ad.interfaceType = algotype;
					// TODO activate this ::  ad.guid = ((AlgorithmIntf)algorithm).getInstanceGuid() ;
					
					algorithms.add(ad) ;
					
					z++;
				}
				
			} catch (ClassNotFoundException e) {
				
			} catch (IllegalAccessException e) {
				
			} catch (InstantiationException e) {
				
			} catch (Exception e) {
				
			} // try
			
			
		} // i ->
		return z;
	}


	public Object createAlgoObject( String algorithmName, int algorithmType) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
		 
		// Class<?> c = null;
		// Method[] mm;
		
		Object algorithm = null;
		
		 
			 
			 
		    algorithm = loadReaderAlgoClass( algorithmName, algorithmType );

		    // we put it to a map for retrieval by name
		    
		    
		    
		 
		
		return algorithm;
	}
	
	
	public Object loadReaderAlgoClass( String algoname, int algorithmType  ) 
																			throws 	ClassNotFoundException,
																					IllegalAccessException, 
																					InstantiationException {

		ClassLoader parentClassLoader = null ;
		AlgoClassLoader classLoader ;

		Class algoObjectClass = null;

		AlgorithmIntf algoGenObject = null;
		
		AlgoColumnWriterIntf algoWriterObject = null;
		
		AlgoMeasurementIntf algoMeasureObject = null;
		AlgoReaderIntf algoReaderObject = null ;
		AlgoAssignatorIntf algoAssignatorObject = null; 
		// else: Visor, Listener, Sensor ...
		
		
		String classToLoad;

		// is it a plugin class ? 
		// TransformOriginator is imported from pluginSettings, where it is set during loading of the plugins
		if (getTransformOriginator().isAlgorithmPluggedin(algoname)) {

			// classToLoad = packagePath + algoname ;

			// use "algoname" to look up in
			// pluginSettings.getLoadedPluginClasses()
			algoObjectClass = getTransformOriginator().getPluginClassByName(algoname);
			classLoader = new AlgoClassLoader(algoObjectClass.getClassLoader());
			parentClassLoader = classLoader.getParent();

			classToLoad = algoObjectClass.getName();

			// TODO and now we have to create a further instance for the next
			// request, otherwise
			// the permanent settings will overlap, and no parallel execution
			// would be possible

		} else {
			classToLoad = packagePath + algoname;

			parentClassLoader = AlgoClassLoader.class.getClassLoader();

			classLoader = new AlgoClassLoader(parentClassLoader);

			algoObjectClass = classLoader.loadClass(classToLoad);
		}

		/*
		 * if (algorithmType == AlgorithmIntf._ALGOTYPE_GENERIC){ // this is
		 * just used for reading/returning the type of the desired algorithm
		 * algoGenObject = (AlgorithmIntf) algoObjectClass.newInstance(); }
		 * 
		 * if (algorithmType == AlgorithmIntf._ALGOTYPE_PASSIVE){
		 * algoMeasureObject = (AlgoMeasurementIntf)
		 * algoObjectClass.newInstance(); } if (algorithmType ==
		 * AlgorithmIntf._ALGOTYPE_VALUE){ algoValueTransformObject =
		 * (AlgoTransformationIntf) algoObjectClass.newInstance(); } if
		 * (algorithmType == AlgorithmIntf._ALGOTYPE_WRITER){ algoWriterObject =
		 * (AlgoColumnWriterIntf) algoObjectClass.newInstance(); }
		 */
		// create new class loader so classes can be reloaded.
		// classLoader = new AlgoClassLoader(parentClassLoader);
		// algoObjectClass = classLoader.loadClass( classToLoad );

		Object algoObject = null;

		AlgorithmIntf basicAlgoInfo = (AlgorithmIntf)algoObjectClass.newInstance();
		int declaredTypeInfo = basicAlgoInfo.getType() ;
		
		
		if (algorithmType == AlgorithmIntf._ALGOTYPE_GENERIC) {
			// this is just used for reading/returning the type of the desired
			// algorithm
			algoGenObject = (AlgorithmIntf) algoObjectClass.newInstance();
			algoObject = (Object) algoGenObject;
		}
		if ((declaredTypeInfo==algorithmType) && (algorithmType == AlgorithmIntf._ALGOTYPE_PASSIVE)) {
			algoMeasureObject = (AlgoMeasurementIntf) algoObjectClass.newInstance();
			algoObject = (Object) algoMeasureObject;
		}
		if ((declaredTypeInfo==algorithmType) && (algorithmType == AlgorithmIntf._ALGOTYPE_VALUE)) {
			// algoValueTransformObject = (AlgoTransformationIntf) algoObjectClass.newInstance();
			// algoObject = (Object) algoValueTransformObject;
		}
		if ((declaredTypeInfo==algorithmType) && (algorithmType == AlgorithmIntf._ALGOTYPE_WRITER)) {
			algoWriterObject = (AlgoColumnWriterIntf) algoObjectClass.newInstance();
			algoObject = (Object) algoWriterObject;
		}
		if ((declaredTypeInfo==algorithmType) && (algorithmType == AlgorithmIntf._ALGOTYPE_READER)) {
			algoReaderObject = (AlgoReaderIntf) algoObjectClass.newInstance();
			algoObject = (Object) algoReaderObject;
		}
		if ((declaredTypeInfo==algorithmType) && (algorithmType == AlgorithmIntf._ALGOTYPE_ASSIGNATOR)) {
			algoAssignatorObject = (AlgoAssignatorIntf) algoObjectClass.newInstance();
			algoObject = (Object) algoAssignatorObject;
		}
		// also visor, listener, etc...
		return algoObject;
	}

	public void setTransformOriginator(TransformationEnvIntf transformoriginator) {
		transformOriginator = transformoriginator;
	}



	public TransformationEnvIntf getTransformOriginator() {
		return transformOriginator;
	}

	
	@Override
	public ArrayList<String> getAlgorithmNames() {
		ArrayList<String> anames = new ArrayList<String>();
		if (algorithms != null){
			// AlgorithmDescription
			for (int i=0;i<algorithms.size();i++){
				String aname = algorithms.get(i).name ;
				anames.add(aname) ;
			}
		}
		return anames;
	}


	@Override
	public Map<String, Object> getAlgorithMap() {
		// 
		return algorithMap;
	}

	
}
